﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NetFrameworkExtensions.Mathematics
{
  public static class MathUtilsExtensions
  {
    public const double IntEpsilon = 1d / int.MaxValue;

    public static int GCD(this int n1, int n2)
    {
      if (n1 < 0) n1 = -n1;
      if (n2 < 0) n2 = -n2;
      if (n2 == 0)
        n2 = n1;
      else
      {
        int r;
        while ((r = n1 % n2) != 0)
        {
          n1 = n2;
          n2 = r;
        }
      }
      return n2;
    }
    public static long GCD(this long n1, long n2)
    {
      if (n1 < 0) n1 = -n1;
      if (n2 < 0) n2 = -n2;
      if (n2 == 0)
        n2 = n1;
      else
      {
        long r;
        while ((r = n1 % n2) != 0)
        {
          n1 = n2;
          n2 = r;
        }
      }
      return n2;
    }

    public static int GreatestCommonDenominator(int n1, int n2)
    {
      return n1.GCD(n2);
    }
    public static long GreatestCommonDenominator(long n1, long n2)
    {
      return n1.GCD(n2);
    }

    public static IEnumerable<int> ToIntContinualFractionEnumerable(double value)
    {
      value = Math.Abs(value);
      yield return (int)value;
      value = (value - (int)value);
      while (value > IntEpsilon)
      {
        value = 1d / value;
        yield return (int)value;
        value = (value - (int)value);
      }
    }

    public static IntContinualFraction ToIntContinualFraction(this double value)
    {
      return new IntContinualFraction(ToIntContinualFractionEnumerable(value));
    }

    public static int CountAllBits(this int number)
    {
      return CountAllBits((uint)number);
    }
    public static int CountOneBits(this int number)
    {
      return CountOneBits((uint)number);
    }
    public static int CountZeroBits(this int number)
    {
      return CountZeroBits((uint)number);
    }
    public static int CountAllBits(this uint number)
    {
      if ((number & 0x80000000) != 0) return 32;
      int count = 0;
      while (number != 0)
      {
        count++;
        number >>= 1;
      }
      return count;
    }
    public static int CountOneBits(this uint number)
    {
      if (number == 0) return 0;
      int count = 0;
      while (number != 0)
      {
        count += (int)(number & 1);
        number >>= 1;
      }
      return count;
    }
    public static int CountZeroBits(this uint number)
    {
      if (number == 0) return 1;
      int count = 0;
      while (number != 0)
      {
        count += 1 - (int)(number & 1);
        number >>= 1;
      }
      return count;
    }

    public static int CountAllBits(this long number)
    {
      return CountAllBits((ulong)number);
    }
    public static int CountOneBits(this long number)
    {
      return CountOneBits((ulong)number);
    }
    public static int CountZeroBits(this long number)
    {
      return CountZeroBits((ulong)number);
    }
    public static int CountAllBits(this ulong number)
    {
      if ((number & 0x8000000000000000L) != 0) return 64;
      int count = 0;
      while (number != 0)
      {
        count++;
        number >>= 1;
      }
      return count;
    }
    public static int CountOneBits(this ulong number)
    {
      if (number == 0) return 0;
      int count = 0;
      while (number != 0)
      {
        count += (int)(number & 1);
        number >>= 1;
      }
      return count;
    }
    public static int CountZeroBits(this ulong number)
    {
      if (number == 0) return 1;
      int count = 0;
      while (number != 0)
      {
        count += 1 - (int)(number & 1);
        number >>= 1;
      }
      return count;
    }

    public static bool IsPowerOfTwo(this int number)
    {
      return ((uint)number).IsPowerOfTwo();
    }
    public static bool IsPowerOfTwo(this uint number)
    {
      return (number & (number - 1)) == 0;
    }
    public static bool IsPowerOfTwo(this long number)
    {
      return ((ulong)number).IsPowerOfTwo();
    }
    public static bool IsPowerOfTwo(this ulong number)
    {
      return (number & (number - 1)) == 0;
    }

    public static int NextPowerOfTwo(this int number)
    {
      if (number.IsPowerOfTwo()) return number;
      return 1 << number.CountAllBits();
    }
    public static uint NextPowerOfTwo(this uint number)
    {
      if (number.IsPowerOfTwo()) return number;
      return (uint)1 << number.CountAllBits();
    }
    public static long NextPowerOfTwo(this long number)
    {
      if (number.IsPowerOfTwo()) return number;
      return 1L << number.CountAllBits();
    }
    public static ulong NextPowerOfTwo(this ulong number)
    {
      if (number.IsPowerOfTwo()) return number;
      return (ulong)1L << number.CountAllBits();
    }
  }
}
